#include "stdafx.h"

#include <windows.h>
#include <process.h>

#include "StdMyLog.h"

#include <map>
using namespace std;

const string DEBUG_LOG_SECTION = "DEBUG LOG";
const string strLOG_IN_THREAD = "LOG_IN_THREAD";

CStdMyLog CStdMyLog::_Instance;
static LogConfig defLogConfig;

CStdMyLog::CStdMyLog(void)
:m_bRuning(false)
{
	memset(&defLogConfig, 0, sizeof(defLogConfig));
	defLogConfig.LogHisLim = 24;
	strcpy( defLogConfig.szFileName, "c:\\");

	defLogConfig.bLogAsync   = false;
	defLogConfig.bOutput	 = true;
	defLogConfig.bOutputDebugView = true;
	defLogConfig.bOutputFile = true;
	defLogConfig.LogHisLim   = 24;
	defLogConfig.nLogLevl    = 4;
	defLogConfig.nLogViewLevl=4;
	strcpy(defLogConfig.szModuleName, "Default");

	char wspFile[260];
	GetWSPConfig(wspFile, 260);

	defLogConfig.bLogAsync = GetPrivateProfileInt(DEBUG_LOG_SECTION.c_str(), strLOG_IN_THREAD.c_str(), 0, wspFile);

	InitializeCriticalSection(&m_csMyBlock);
	InitializeCriticalSection(&m_csQueLog);

	m_hEvtQueNEmpty = CreateEvent(NULL, FALSE, FALSE, NULL);

	//StartRunT();
}

CStdMyLog::~CStdMyLog(void)
{
	StopRunT();
	DeleteCriticalSection(&m_csMyBlock);
	DeleteCriticalSection(&m_csQueLog);
}

CStdMyLog& CStdMyLog::GetInstance()
{
	return _Instance;
}
LogConfig* CStdMyLog::DefLogConfig()
{
	return &defLogConfig;
}
void CStdMyLog::SetUp(LogConfig& config, const char* szFilePath, int nLogLevl, int nLogViewLevel,bool bOutput, 
					  bool bOutputDebugView, bool bOutputFile, int LogHisLim)
{
	config.nLogViewLevl = nLogViewLevel;
	config.nLogLevl     = nLogLevl;
	
	strncpy(config.szFileName, szFilePath, sizeof(config.szFileName)-1);
	
	config.bOutput      = bOutput;
	config.bOutputDebugView = bOutputDebugView;
	config.bOutputFile  = bOutputFile;
	config.LogHisLim    = LogHisLim;
}

std::string CStdMyLog::GetTypeString(int LogType)const
{
	switch(LogType)
	{
	case LOG_TRACE:
		return "TRACE";
	case LOG_DEBUG_:
		return "DEBUG";
	case LOG_INFO:
		return "INFO";
	case LOG_WARNING_:
		return "WARNING";
	case LOG_ERROR:
		return "ERROR";
	default:
		return "TRACE";
	}
}

bool CStdMyLog::ShouldLog(const LogConfig& config, int nLog)
{
	if(nLog <= config.nLogLevl)
		return true;
	return false;
}
bool CStdMyLog::ShouldLogView(const LogConfig& config, int nLog)
{
	if(nLog <= config.nLogViewLevl)
		return true;
	return false;
}

void CStdMyLog::MyLogInfo(LogConfig& config, const char* modulename,const char* classname,const char* memfunc,int logType, SYSTEMTIME* time, const TCHAR* pzFormat, ...)
{
	char szFullName[8192];
	char szBuffer[8192] = "";
	int  nStrLen = 0;
	SYSTEMTIME curTime;

	if(time)
		curTime = *time;
	else
		::GetLocalTime(&curTime);
	if( config.bOutput )
	{
		_stprintf(szBuffer+nStrLen, _T("[%04d-%02d-%02d %02d:%02d:%02d.%03d],"), curTime.wYear, curTime.wMonth, curTime.wDay, 
			curTime.wHour, curTime.wMinute, curTime.wSecond, curTime.wMilliseconds);
		nStrLen = _tcslen(szBuffer);
		_stprintf(szBuffer+nStrLen, _T("[%#04x],"),GetCurrentThreadId());

		nStrLen = _tcslen(szBuffer);
		_stprintf(szBuffer+nStrLen, _T("[%s],"),modulename);

		std::string szType = GetTypeString(logType);
		nStrLen = _tcslen(szBuffer);
		_stprintf(szBuffer+nStrLen, _T("[%s],"),szType.c_str());

		strcat(szBuffer,pzFormat);

		nStrLen = _tcslen(szBuffer);
		_stprintf(szBuffer+nStrLen, _T(",[%s.%s]\r\n"), classname, memfunc);

		// Output to file
		if( config.bOutputFile && ShouldLog(config,logType) ) 
		{
			CHAR szFileNameWithDate[MAX_PATH] = _T("");
			_stprintf(szFileNameWithDate, _T("%04d%02d%02d%02d - %s"), 
				curTime.wYear, curTime.wMonth, curTime.wDay, curTime.wHour, config.szFileName);
			sprintf_s(szFullName, "%s\\%s", config.szFileName, szFileNameWithDate);
			
			EnterCriticalSection(&m_csMyBlock);
			{
				FILE* fp = fopen(szFullName,"a");
				if(fp){
					_ftprintf(fp,"%s",szBuffer);
					fclose(fp);
					if( strcmp(szFullName,config.szFullName) != 0 )
					{
						strcpy(config.szFullName, szFullName);
						DoLogHisLimit(config);
					}
				}
			}
			LeaveCriticalSection(&m_csMyBlock);
		}
		if( config.bOutputDebugView && ShouldLogView(config,logType) )
		{
			OutputDebugString(szBuffer);
		}	
	}
}
void CStdMyLog::OutputLog(LogConfig& config, int logType, const char* pszMsg)
{
	char szFullName[MAX_PATH] = "";
	char szBuffer[MAX_PATH] = "";

	SYSTEMTIME curTime;
	::GetLocalTime(&curTime);

	if( config.bOutput )
	{
		std::string szType = GetTypeString(logType);

		CHAR szFileNameWithDate[MAX_PATH] = _T("");
		sprintf_s(szFileNameWithDate, _T("%04d%02d%02d%02d - %s"), 
			curTime.wYear, curTime.wMonth, curTime.wDay, curTime.wHour, config.szFileName);
		sprintf_s(szFullName, "%s\\%s", config.szFileName, szFileNameWithDate);

		char szBuffer[8192];
		strncpy(szBuffer, pszMsg, sizeof(szBuffer));
		szBuffer[sizeof(szBuffer)-1] = 0;

		// Output to file
		if( config.bOutputFile && ShouldLog(config, logType) ) 
		{
			EnterCriticalSection(&m_csMyBlock);
			{
				FILE* fp = fopen(szFullName, "a");
				if (fp) {
					fprintf_s(fp, ("[%04d-%02d-%02d %02d:%02d:%02d:%03d] [%s]: %s\n"), 
						curTime.wYear, curTime.wMonth, curTime.wDay, curTime.wHour, curTime.wMinute,
						curTime.wSecond, curTime.wMilliseconds, szType.c_str(), szBuffer);
					fclose(fp);
				}
			}
			LeaveCriticalSection(&m_csMyBlock);
		}

		// Output to debug window
		if( config.bOutputDebugView && ShouldLogView(config, logType) )
		{
			{
				strcat(szBuffer, ("\r\n"));
				CHAR szBufferTmp[8192];
				sprintf_s(szBufferTmp, ("[%s]%s"), szType.c_str(), szBuffer);
				OutputDebugString(szBufferTmp);
			}
		}
	}
}
void CStdMyLog::OutputLog(LogConfig& config, const char* classname,const char* memfunc,int logType, SYSTEMTIME* time, DWORD dwThreadID, const char* pszMsg)
{
	char szFullName[8192];
	char szBuffer[8192] = "";

	SYSTEMTIME curTime;
	if( NULL == time ) {
		::GetLocalTime(&curTime);
		time = &curTime;
	}
		
	if( config.bOutput )
	{
		_stprintf(szBuffer, _T("[%04d-%02d-%02d %02d:%02d:%02d.%03d],"), time->wYear, time->wMonth, time->wDay, 
			time->wHour, time->wMinute, time->wSecond, time->wMilliseconds);
		std::string strOutput(szBuffer);

		_stprintf(szBuffer, _T("[%#04x],"),dwThreadID);
		strOutput += szBuffer;

		_stprintf(szBuffer, _T("[%s],"), config.szModuleName);
		strOutput += szBuffer;

		std::string szType = GetTypeString(logType);
		_stprintf(szBuffer, _T("[%s],"),szType.c_str());
		strOutput += szBuffer;

		strcat(szBuffer,pszMsg);
		strOutput += szBuffer;

		_stprintf(szBuffer, _T(",[%s.%s]\r\n"), classname, memfunc);
		strOutput += szBuffer;

		// Output to file
		if( config.bOutputFile && ShouldLog(config,logType) ) 
		{
			TCHAR szFileNameWithDate[MAX_PATH] = _T("");
			_stprintf(szFileNameWithDate, _T("%04d%02d%02d%02d - %s.txt"), 
				curTime.wYear, curTime.wMonth, curTime.wDay, curTime.wHour, config.szModuleName);
			sprintf_s(szFullName, "%s\\%s", config.szFileName, szFileNameWithDate);

			EnterCriticalSection(&m_csMyBlock);
			{
				FILE* fp = fopen(szFullName,"a");
				if(fp){
					_ftprintf(fp,"%s",strOutput.c_str());
					fclose(fp);
					if( strcmp(szFullName,config.szFullName) != 0 )
					{
						strcpy(config.szFullName, szFullName);
						DoLogHisLimit(config);
					}
				}
			}
			LeaveCriticalSection(&m_csMyBlock);
		}
		if( config.bOutputDebugView && ShouldLogView(config,logType) )
		{
			OutputDebugString(strOutput.c_str());
		}	
	}
}

static int FindFilesInDir(LPCSTR szPath,LPCSTR szName, list<string>& filelst);
void CStdMyLog::DoLogHisLimit(const LogConfig& config)const
{
	list<string> filelst;
	FindFilesInDir(config.szFileName, config.szModuleName, filelst);
	filelst.sort();
	list<string>::iterator iter = filelst.begin();
	int cutterLen = filelst.size() - config.LogHisLim;
	for(;iter!=filelst.end()&&cutterLen>0; iter++)
	{
		DeleteFile(iter->c_str());
		cutterLen--;
	}
}
void CStdMyLog::Run()
{
	LogPackage msg;
	while(DelQueryLogQue(&msg))
	{
		string classname = msg.m_szclass_memfunc;
		string funcmem   = classname.substr(classname.find_last_of(':')+1);
		classname.erase(classname.find_first_of(':'));

		msg.m_szBuffer.erase( 0, msg.m_szBuffer.find_first_not_of(' ') );
		msg.m_szBuffer.erase( msg.m_szBuffer.find_last_not_of(" \n\r")+1 );

		OutputLog(msg.Info, classname.c_str(),funcmem.c_str(),msg.m_nlogType,&msg.time, msg.m_nThreadID, msg.m_szBuffer.c_str());
	}
	WaitForSingleObject(m_hEvtQueNEmpty,3000);
}
void CStdMyLog::LogThread(void* param)
{
	CStdMyLog* pThis = static_cast<CStdMyLog*>(param);
	pThis->Run();
}
void CStdMyLog::StartRunT()
{
	if(!m_bRuning){
		m_bRuning = true;
		m_ThdMng.RegisterCallBk(LogThread,this);
		m_ThdMng.Start();
	}
}
void CStdMyLog::StopRunT()
{
	if(m_bRuning){
		m_bRuning = false;
		m_ThdMng.Stop();
	}
}
void CStdMyLog::AddLog2Que(const LogConfig& config, LogPackage* pack)
{
	if(pack->m_nlogType>=10)
		pack->m_nlogType = LOG_TRACE;
	if( ShouldLog(config, pack->m_nlogType) || ShouldLogView(config, pack->m_nlogType) ){
		bool flag = false;
		Lock_QueLog();
		if(m_queLog.empty())
			flag = true;
		if(m_queLog.size()<220000)
			m_queLog.push_back(*pack);
		Unlock_QueLog();
		if(flag){
			SetEvent(m_hEvtQueNEmpty);
		}
	}
}
bool CStdMyLog::DelQueryLogQue(LogPackage* pack)
{
	bool ret = false;
	
	Lock_QueLog();
	if(!m_queLog.empty())
	{
		ret = true;
		*pack = m_queLog.front();
		m_queLog.pop_front();
	}
	Unlock_QueLog();
	
	return ret;
}
void ThreadMng::RunThread()
{
	while(true)
	{
		if (WaitForSingleObject(m_hEvtEnd, m_nInterval) == WAIT_OBJECT_0)
		{
			break;
		}
		if(m_fncallback)
			m_fncallback(m_fnparam);
	}
	ResetEvent(m_hEvtEnd);
}

UINT WINAPI ThreadMng::ThreadProc(LPVOID pParam)
{
	ThreadMng *pThread = (ThreadMng *)pParam;
	pThread->RunThread();
	return 0;
}
ThreadMng::ThreadMng()
:m_nInterval(1000)
,m_hThread(NULL)
//,m_uiThreadId(0)
{
	m_hEvtEnd = CreateEvent(NULL,TRUE,FALSE,NULL);
	m_fncallback = NULL;
	m_fnparam = NULL;
}
ThreadMng::~ThreadMng()
{
	Stop();
}
void ThreadMng::RegisterCallBk(func fnCallBack,void* parameter)
{
	m_fnparam = parameter;
	m_fncallback = fnCallBack;
}
void ThreadMng::Start(int nInterval)
{
	m_nInterval = nInterval;
	//HANDLE hThread;
	//UINT uiThreadId = 0;
	m_hThread = (HANDLE)CreateThread(NULL,  // Security attributes
		0,    // stack
		(LPTHREAD_START_ROUTINE)ThreadMng::ThreadProc,   // Thread proc
		this,   // Thread param
		CREATE_SUSPENDED,   // creation mode
		NULL);   // Thread ID

	if ( NULL != m_hThread)
	{
		//m_uiThreadId = uiThreadId;
		//SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL);
		ResumeThread( m_hThread );
		return ;
	}
}
void ThreadMng::Stop()
{
	if( NULL == m_hEvtEnd ){
		//m_uiThreadId = 0;
		return ;
	}
	::SetEvent(m_hEvtEnd);
 	//if(m_uiThreadId)
	{
		if(WaitForSingleObject(m_hThread,20000)==WAIT_TIMEOUT){
			TerminateThread(m_hThread,0);
			//MyLogInfo("ThreadMng::Stop",LOG_TRACE,"TerminateThread",NULL);
		}
	}
	//while (WaitForSingleObject(m_hThread, 0) == WAIT_OBJECT_0)
	//	Sleep(5);
	CloseHandle(m_hThread);

	//m_uiThreadId = 0;
	m_hThread    = NULL;
	m_fncallback = NULL;
	m_fnparam    = NULL;
}



static int FindFilesInDir(LPCSTR szPath,LPCSTR szName, list<string>& filelst)
{
	filelst.clear();

	char szTmp[MAX_PATH]="";
	WIN32_FIND_DATA fd;
	lstrcpy(szTmp,szPath);
	if(szTmp[lstrlen(szTmp)-1] != '\\')
		lstrcat(szTmp, "\\");
	lstrcat(szTmp,"*.*");
	HANDLE hd=FindFirstFile(szTmp,&fd);
	lstrcpy(szTmp,szPath);
	if(szTmp[lstrlen(szTmp)-1] != '\\')
		lstrcat(szTmp,"\\");

	if(hd != INVALID_HANDLE_VALUE)
	{
		do
		{
			lstrcpy(szTmp,szPath);
			if(szTmp[lstrlen(szTmp)-1] != '\\')
				lstrcat(szTmp,"\\");

			if(fd.dwFileAttributes!=FILE_ATTRIBUTE_DIRECTORY)
			{
				lstrcat(szTmp,fd.cFileName);
				string strFileName = fd.cFileName;
				int nSpace = strFileName.find_last_of(' ');
				int nEnd = strFileName.find_last_of('.');
				strFileName = strFileName.substr( nSpace + 1, nEnd - nSpace -1);
				if(  lstrlen(szTmp)>4 && lstrcmp(szTmp+lstrlen(szTmp)-4,".txt")==0 &&  lstrcmp(strFileName.c_str(), szName) == 0)
					filelst.push_back(szTmp);
			}

		}while(FindNextFile(hd, &fd));
		FindClose(hd);
	}
	SimpleLogInfo(NULL, LOG_INFO, "finish");
	return 1;
}


